package com.kkwrite.regimen.provider.service.income;

import com.kkwrite.regimen.common.constant.DeletedEnum;
import com.kkwrite.regimen.common.constant.ShopIncomeEnum;
import com.kkwrite.regimen.common.entity.dto.income.IncomeCountOutDTO;
import com.kkwrite.regimen.provider.domain.RegShopIncomeDO;
import com.kkwrite.regimen.provider.repository.RegShopIncomeRepository;
import com.kkwrite.regimen.provider.util.EntityManagerUtil;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import javax.persistence.Tuple;
import javax.persistence.criteria.Predicate;

/**
 * Created by Ron on 2018/9/12 16:42.
 *
 * @author Ron
 */
@Service("shopIncomeService")
@Transactional(rollbackFor = Exception.class)
public class ShopIncomeServiceImpl implements ShopIncomeService {

    private static final double FEE_RATIO = 0.01;
    private static final double MIN_EXTRACT_CASH_AMOUNT = 1;

    @Resource
    private EntityManagerUtil<RegShopIncomeDO> entityManagerUtil;
    @Resource
    private RegShopIncomeRepository regShopIncomeRepository;

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void extractCashOfShop(Double amount, Long shopId) {
        if (amount < MIN_EXTRACT_CASH_AMOUNT) {
            throw new RuntimeException("小于最低提现金额");
        }
        Double total = findCanExtractCashAmount(shopId);
        if (amount > total) {
            throw new RuntimeException("可用余额不足");
        }
        RegShopIncomeDO income = new RegShopIncomeDO();
        income.setShopId(shopId);
        income.setIncomeSourceType((short) 3);
        income.setIncomeStatus((short) 1);
        income.setIncomeAmount(-amount);
        income.setDeleted(DeletedEnum.UN_DELETED.getValue());
        // 手续费
        double fee = amount * FEE_RATIO;
        double actualAmount = (amount * 100 - fee * 100) / 100;
        income.setHandlingFee(fee);
        income.setActualAmount(actualAmount);
        income = regShopIncomeRepository.saveAndFlush(income);
        if (income.getIncomeId() == null) {
            throw new RuntimeException("服务异常，提现申请失败");
        }
    }

    @Transactional(rollbackFor = Exception.class, readOnly = true)
    public Double findCanExtractCashAmount(Long shopId) {
        Tuple tuple = entityManagerUtil.singleQuery(
                RegShopIncomeDO.class,
                (cb, root) -> cb.tuple(cb.sum(root.get("incomeAmount").as(Double.class)).alias("total")),
                (cb, root) -> {
                    Predicate p1 = cb.equal(root.get("shopId"), shopId);
                    Predicate p2 = cb.equal(root.get("incomeStatus"), ShopIncomeEnum.Status.NORMAL.getStatus());
                    Predicate p3 = cb.equal(root.get("incomeStatus"), ShopIncomeEnum.Status.PENDING_PROCESS.getStatus());
                    Predicate p4 = cb.lt(root.get("incomeAmount"), 0);
                    return cb.and(p1, cb.or(p2, cb.and(p3, p4)));
                });
        Double total = tuple.get("total", Double.class);
        if (total == null) {
            return 0.0;
        }
        return total;
    }

    @Override
    @Transactional(rollbackFor = Exception.class, readOnly = true)
    public IncomeCountOutDTO count(String shopIdString) {
        int shopId = Integer.parseInt(shopIdString);
        IncomeCountOutDTO out = new IncomeCountOutDTO();
        out.setInTotal(getIncomeInTotal(shopId));
        out.setOutTotal(getIncomeOutTotal(shopId));
        out.setTotal((out.getInTotal() * 100 + out.getOutTotal() * 100) / 100.0);
        out.setExtractCashPendingProcess(getIncomeExtractPending(shopId));
        return out;
    }

    @Transactional(rollbackFor = Exception.class, readOnly = true)
    public Double getIncomeInTotal(int shopId) {
        Tuple tuple = entityManagerUtil.singleQuery(
                RegShopIncomeDO.class,
                (cb, root) -> cb.tuple(cb.sum(root.get("incomeAmount").as(Double.class)).alias("total")),
                (cb, root) -> cb.and(cb.equal(root.get("shopId").as(Long.class), shopId),
                        cb.notEqual(root.get("incomeStatus").as(Short.class), ShopIncomeEnum.Status.CANCEL.getStatus()),
                        cb.gt(root.get("incomeAmount"), 0)));
        Double total = tuple.get("total", Double.class);
        if (total == null) {
            return 0.0;
        }
        return total;
    }

    @Transactional(rollbackFor = Exception.class, readOnly = true)
    public Double getIncomeOutTotal(int shopId) {
        Tuple tuple = entityManagerUtil.singleQuery(
                RegShopIncomeDO.class,
                (cb, root) -> cb.tuple(cb.sum(root.get("incomeAmount").as(Double.class)).alias("total")),
                (cb, root) -> cb.and(cb.equal(root.get("shopId").as(Long.class), shopId),
                        cb.notEqual(root.get("incomeStatus").as(Short.class), ShopIncomeEnum.Status.CANCEL.getStatus()),
                        cb.lt(root.get("incomeAmount"), 0)));
        Double total = tuple.get("total", Double.class);
        if (total == null) {
            return 0.0;
        }
        return total;
    }

    @Transactional(rollbackFor = Exception.class, readOnly = true)
    public Double getIncomeExtractPending(int shopId) {
        Tuple tuple = entityManagerUtil.singleQuery(
                RegShopIncomeDO.class,
                (cb, root) -> cb.tuple(cb.sum(root.get("incomeAmount").as(Double.class)).alias("total")),
                (cb, root) -> cb.and(cb.equal(root.get("shopId").as(Long.class), shopId),
                        cb.equal(root.get("incomeStatus").as(Short.class), ShopIncomeEnum.Status.PENDING_PROCESS.getStatus()),
                        cb.equal(root.get("incomeSourceType").as(Short.class), ShopIncomeEnum.SourceType.EXTRACT_CASH.getType())));
        Double total = tuple.get("total", Double.class);
        if (total == null) {
            return 0.0;
        }
        return total;
    }

}
